package com.carefree.system.modules.admin.service.impl;

import com.alibaba.fastjson2.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.carefree.common.constant.UserConstant;
import com.carefree.common.core.PageUtils;
import com.carefree.common.core.Result;
import com.carefree.common.emus.StatusCodeEnum;
import com.carefree.common.exception.BusinessException;
import com.carefree.common.utils.HttpContextUtils;
import com.carefree.common.utils.IpUtils;
import com.carefree.common.utils.JwtUtils;
import com.carefree.system.common.utils.SecurityUtils;
import com.carefree.system.modules.admin.dao.UserDao;
import com.carefree.system.modules.admin.entity.RoleEntity;
import com.carefree.system.modules.admin.entity.UserEntity;
import com.carefree.system.modules.admin.entity.UserRoleEntity;
import com.carefree.system.modules.admin.service.*;
import com.carefree.system.modules.admin.vo.AssginRoleVo;
import com.carefree.system.modules.admin.vo.LoginVo;
import com.carefree.system.modules.admin.vo.RouterVo;
import com.carefree.system.modules.admin.vo.UserQueryVo;
import com.carefree.system.modules.security.entity.CustomUserDetails;
import com.carefree.system.modules.security.entity.RedisUserDetails;
import org.apache.commons.lang3.StringUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;


/**
 * 用户
 *
 * @author tengbin
 */
@Service("userService")
public class UserServiceImpl extends ServiceImpl<UserDao, UserEntity> implements UserService {

    @Resource
    private UserDao userDao;

    @Resource
    private UserRoleService userRoleService;

    @Resource
    private RoleService roleService;

    @Resource
    private MenuService menuService;

    @Resource
    private StringRedisTemplate stringRedisTemplate;

    @Resource
    private PasswordEncoder passwordEncoder;

    @Resource
    private AuthenticationManager authenticationManager;

    @Resource
    private LoginLogService loginLogService;

    @Resource
    private SecurityUtils securityUtils;


    /**
     * 使用自定义vo对象返回结果并显示
     *
     * @param params
     * @return
     */
    @Override
    public PageUtils queryPageForVo(Map<String, Object> params) {
        // 搜索框
        String keyword = (String) params.get("keyword");
        // 时间范围
        String createTimeBegin = (String) params.get("createTimeBegin");
        String createTimeEnd = (String) params.get("createTimeEnd");
        // 分页参数
        int currPage = Integer.parseInt((String) params.get("page"));
        int pageSize = Integer.parseInt((String) params.get("limit"));
        int totalCount = userDao.selectCountUserPage("%" + keyword + "%", createTimeBegin, createTimeEnd);

        List<UserQueryVo> userQueryVos = userDao.selectUserPage("%" + keyword + "%", createTimeBegin, createTimeEnd, (currPage - 1) * pageSize, pageSize);

        return new PageUtils(userQueryVos, totalCount, pageSize, currPage);
    }

    /**
     * 启用或禁用
     *
     * @param id     用户id
     * @param status 状态0/1
     * @return
     */
    @Override
    public boolean activeOrInActiveById(Long id, Integer status) {
        if (Objects.isNull(id) || Objects.isNull(status)) {
            return false;
        }
        UserEntity userEntity = getById(id);
        if (userEntity != null) {
            userEntity.setStatus(status);
            return updateById(userEntity);
        }
        return false;
    }

    /**
     * 保存用户信息
     *
     * @param user
     * @return
     */
    @Override
    public boolean saveUser(UserEntity user) {
        user.setPassword(passwordEncoder.encode(user.getPassword()));
        return save(user);
    }

    /**
     * 修改用户信息
     *
     * @param user
     * @return
     */
    @Override
    public boolean updateUser(UserEntity user) {
        Long userId = user.getId();
        String password = user.getPassword();

        String inputPassword = passwordEncoder.encode(password);
        String queryPassword = getOne(new QueryWrapper<UserEntity>().eq("id", userId)).getPassword();
        user.setPassword(StringUtils.isNotBlank(password) ? inputPassword : queryPassword);
        return updateById(user);
    }

    /**
     * 根据查询到的用户信息获取相关用户信息
     *
     * @param user
     * @return
     */
    @Override
    public Map<String, Object> getUserInfo() {
        Long userId = securityUtils.getPrincipalForUserId();
        Map<String, Object> resultMap = new HashMap<>(7);

        // 角色信息
        List<RoleEntity> roles = roleService.queryRoleInfoByUserId(userId);
        resultMap.put("roles", roles);
        // 菜单权限数据
        List<RouterVo> routerVos = menuService.getMenusByUserId(userId);
        resultMap.put("routers", routerVos);
        // 按钮权限数据
        List<String> buttons = menuService.getMenuButtonsByUserId(userId);
        resultMap.put("buttons", buttons);

        resultMap.put("userId", userId);
        resultMap.put("name", securityUtils.getPrincipalForUserName());

        // 根据当前的用户id查询headUrl,description
        UserEntity user = userDao.selectHeadUrlAndDescriptionById(userId);
        resultMap.put("avatar", user.getHeadUrl());
        resultMap.put("introduction", user.getDescription());

        return resultMap;
    }

    /**
     * 用户登录
     * // 使用AuthenticationManager对用户进行认证---步骤:
     * //   1.在SecuirtyConfig中@Bean注入并重写AuthenticationManager
     * //   2.在当前service实现类中@Autowired引入AuthenticationManager
     * //   3.调用其中的authenticate()方法
     * // 如果不按照上述步骤来,那么就必须:
     * //  1.自定义一个类并且extends UsernamePasswordAuthenticationFilter
     * //  2.重写三个方法: 认证->attemptAuthentication(),认证成功->successfulAuthentication(),认证失败->unsuccessfulAuthentication()方法
     *
     * @param loginVo
     * @return
     */
    @Override
    public Result<Object> accountLogin(LoginVo loginVo) {
        UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(loginVo.getUsername(), loginVo.getPassword());
        Authentication authenticate = authenticationManager.authenticate(authenticationToken);

        // 1.获取已验证成功的认证信息
        CustomUserDetails customUserDetails = (CustomUserDetails) authenticate.getPrincipal();

        Long userId = customUserDetails.getUserEntity().getId();
        String username = customUserDetails.getUserEntity().getUsername();

        // 2.根据用户id,用户名生成jwt
        Map<String, Object> resultMap = new HashMap<>(1);
        String token = JwtUtils.createToken(userId, username);
        resultMap.put("token", token);

        // 3.将验证成功的用户验证信息存入到redis中-> key: login:用户名, value:RedisUserDetails(token+权限列表) ,过期时间为token的有效时间
        RedisUserDetails redisUserDetails = new RedisUserDetails();
        redisUserDetails.setToken(token);
        redisUserDetails.setAuthorities((List<SimpleGrantedAuthority>) authenticate.getAuthorities());

        stringRedisTemplate.opsForValue().set(UserConstant.AUTH_USER_PREFIX + username, JSONObject.toJSONString(redisUserDetails), JwtUtils.TOKEN_EXPIRATION, TimeUnit.MILLISECONDS);

        // 4.保存登录日志
        HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
        loginLogService.saveLoginLog(username, IpUtils.getIpAddress(request), 0, "登录成功!");

        return Result.success(resultMap);
    }


    /**
     * 退出登录
     *
     * @return
     */
    @Override
    public Result<Object> logout() {
        String userName = securityUtils.getPrincipalForUserName();
        // 删除存在redis中的认证信息
        return Boolean.TRUE.equals(stringRedisTemplate.delete(UserConstant.AUTH_USER_PREFIX + userName)) ? Result.ok() : Result.error();
    }

    /**
     * 给用户分配角色
     *
     * @param assginRoleVo
     * @return
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public boolean assignRolesByUserId(AssginRoleVo assginRoleVo) {
        if (Objects.isNull(assginRoleVo)) {
            throw new BusinessException(StatusCodeEnum.DATA_ERROR);
        }
        Long userId = assginRoleVo.getUserId();
        List<Long> roleIds = assginRoleVo.getUserRoleIds();

        // 查询前端传过来的userId是否存在
        UserEntity queryUser = getOne(new QueryWrapper<UserEntity>().eq("id", userId));

        if (queryUser != null) {
            // 1.先删除userId与roleId之前的数据
            userRoleService.remove(new QueryWrapper<UserRoleEntity>().eq("user_id", userId));

            // 2.获取vo对象中的roleId集合,遍历集合然后挨个赋值挨个插入到数据库中
            if (! CollectionUtils.isEmpty(roleIds)) {
                // 遍历Long类型的角色id集合,然后收集成一个对象集合
                List<UserRoleEntity> needSaveUserRoleList = roleIds.stream().map(roleId -> {
                    UserRoleEntity userRoleEntity = new UserRoleEntity();
                    userRoleEntity.setUserId(userId);
                    userRoleEntity.setRoleId(roleId);
                    return userRoleEntity;
                }).collect(Collectors.toList());
                return userRoleService.saveBatch(needSaveUserRoleList);
            }
        }
        return false;
    }


}
